﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace cloud_store_rest_api
{
    // ReSharper disable InconsistentNaming
    public class DropBoxAPI : ICloudStoreRestAPI
    // ReSharper restore InconsistentNaming
    {
        private const string OauthConsumerKey = "oauth_consumer_key";
        private const string OauthSignatureMethod = "oauth_signature_method";
        private const string OauthTimestamp = "oauth_timestamp";
        private const string OauthNonce = "oauth_nonce";
        private const string OauthToken = "oauth_token";
        private const string OauthVersion = "oauth_version";
        private const string OauthSignature = "oauth_signature";
        private const string ApiBaseUrl = "https://api-content.dropbox.com/1/";

        private readonly string _appKey;
        private readonly string _appSecret;
        private readonly string _accessToken;
        private readonly string _accessSecret;

        private readonly IWebClient _webClient;
        private readonly IHMACSHA1 _hash;
        protected const string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

        public DropBoxAPI(string appKey, string appSecret, string accessToken, string accessSecret,
            IWebClient webClient, IHMACSHA1 hash)
        {
            _webClient = webClient;
            _hash = hash;

            _appKey = appKey;
            _appSecret = appSecret;
            _accessToken = accessToken;
            _accessSecret = accessSecret;
        }

        public void GetFileStream(string fileName, Action<Stream> callback)
        {
            var parameters = new Dictionary<string, string>
            {
                {OauthConsumerKey, _appKey},
                {OauthSignatureMethod, "HMAC-SHA1"},
                {OauthTimestamp, GetTimestamp()},
                {OauthNonce, Guid.NewGuid().ToString().Replace("-", "")},
                {OauthToken, _accessToken},
                {OauthVersion, "1.0"}
            };

            var fileApiUrl = string.Format("{0}files/sandbox/{1}", ApiBaseUrl, fileName);
            var oAuthHeader = GetOAuthHeader(parameters, "GET", new Uri(fileApiUrl).AbsoluteUri, _appSecret, _accessSecret);
            var uri = new UriBuilder(fileApiUrl) { Query = oAuthHeader };

            _webClient.OpenReadAsync(uri.Uri, callback);
        }

        public void GetFileString(string fileName, Action<string> callback)
        {
            var parameters = new Dictionary<string, string>
            {
                {OauthConsumerKey, _appKey},
                {OauthSignatureMethod, "HMAC-SHA1"},
                {OauthTimestamp, GetTimestamp()},
                {OauthNonce, Guid.NewGuid().ToString().Replace("-", "")},
                {OauthToken, _accessToken},
                {OauthVersion, "1.0"}
            };

            var fileApiUrl = string.Format("{0}files/sandbox/{1}", ApiBaseUrl, fileName);
            var oAuthHeader = GetOAuthHeader(parameters, "GET", new Uri(fileApiUrl).AbsoluteUri, _appSecret, _accessSecret);
            var uri = new UriBuilder(fileApiUrl) { Query = oAuthHeader };

            _webClient.DownloadStringAsync(uri.Uri, callback);
        }

        protected string GetOAuthHeader(Dictionary<string, string> parameters, string httpMethod, string url, string consumerSecret, string tokenSecret)
        {
            var concat = new StringBuilder();

            var oAuthHeader = new StringBuilder();

            foreach (string k in parameters.Keys.OrderBy(x => x))
            {
                concat.Append(string.Format("{0}={1}&", k, parameters[k]));
                oAuthHeader.Append(string.Format("{0}={1}&", k, parameters[k]));
            }

            concat = concat.Remove(concat.Length - 1, 1);

            byte[] content = Encoding.UTF8.GetBytes(string.Format("{0}&{1}&{2}", httpMethod, EncodeToUpper(url), EncodeToUpper(concat.ToString())));
            byte[] hashedContent = _hash.Hash(Encoding.UTF8.GetBytes(consumerSecret + "&" + tokenSecret), content);

            oAuthHeader.AppendFormat("{0}={1}", OauthSignature, EncodeToUpper(Convert.ToBase64String(hashedContent).Replace("-", "")));
            //OAuthHeader.AppendFormat("{0}={1}", OauthSignature, EncodeToUpper(Convert.ToBase64String(hashedContent).Replace("-", "")) + "");

            return oAuthHeader.ToString();
        }

        protected string GetTimestamp()
        {
            return Convert.ToString((int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
        }

        protected string EncodeToUpper(string raw)
        {
            var result = new StringBuilder();
            foreach (char symbol in raw)
            {
                if (UnreservedChars.IndexOf(symbol) != -1)
                {
                    result.Append(symbol);
                }
                else
                {
                    result.Append('%' + String.Format("{0:X2}", (int)symbol));
                }
            }
            return result.ToString();
        }
    }
}
